#include "glWidget.h"
#include <QKeyEvent>
#include <QDebug>
#include <cmath>

double PI(4*std::atan(1));

GLWidget::GLWidget(QWidget* parent, unsigned int fps)
  : QGLWidget(parent),
    mFramesPainted(0),
    mBottomLayer(0),
    mTopLayer(0),
    mMask(0),
    mZRot(0),
    mMasking(true)
{
  connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateGL()));
  mTimer.start(1000/fps);
  mTime.start();
  setFocusPolicy(Qt::StrongFocus);

  qDebug() << "GLWidget started";
}

GLWidget::~GLWidget()
{
  mTimer.stop();
}

void GLWidget::initializeGL()
{
  loadGLTexture("bottomlayer.png", mBottomLayer);
  loadGLTexture("toplayer.png", mTopLayer);
  loadGLTexture("mask.png", mMask);
  glEnable(GL_TEXTURE_2D); //enable 2D texture mapping

  glShadeModel(GL_SMOOTH); //smooth shading
  glClearColor(0, 0, 0, 0.5); //black background

  //depth buffer setup
  glClearDepth(1);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //best perspective correction
}

void GLWidget::resizeGL(int w, int h)
{
  glViewport(0, 0, w, h); //reset viewport

  //setup perspective view
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45, w*1./h, 0.1, 100.);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void GLWidget::paintGL()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen and depth buffer
  glLoadIdentity(); //reset view

  glTranslatef(0, 0, -2);
  glBindTexture(GL_TEXTURE_2D, mBottomLayer);
  glBegin(GL_QUADS);
    glTexCoord2f(0, -mZRot); glVertex3f(-1.1, -1.1, 0);
    glTexCoord2f(3, -mZRot); glVertex3f(1.1, -1.1, 0);
    glTexCoord2f(3, -mZRot+3); glVertex3f(1.1, 1.1, 0);
    glTexCoord2f(0, -mZRot+3); glVertex3f(-1.1, 1.1, 0);
  glEnd();

  glEnable(GL_BLEND);
  glDisable(GL_DEPTH_TEST);
  if (mMasking)
  {
    glBlendFunc(GL_DST_COLOR, GL_ZERO);
  }
  glTranslatef(0, 0, -1);
  glRotatef(mZRot*360, 0, 0, 1);
  if (mMasking)
  {
    glBindTexture(GL_TEXTURE_2D, mMask);
    glBegin(GL_QUADS);
      glTexCoord2f(0, 0); glVertex3f(-1.1, -1.1, 0);
      glTexCoord2f(1, 0); glVertex3f(1.1, -1.1, 0);
      glTexCoord2f(1, 1); glVertex3f(1.1, 1.1, 0);
      glTexCoord2f(0, 1); glVertex3f(-1.1, 1.1, 0);
    glEnd();
  }
  glBlendFunc(GL_ONE, GL_ONE);
  glBindTexture(GL_TEXTURE_2D, mTopLayer);
  glBegin(GL_QUADS);
    glTexCoord2f(0, 0); glVertex3f(-1.1, -1.1, 0);
    glTexCoord2f(1, 0); glVertex3f(1.1, -1.1, 0);
    glTexCoord2f(1, 1); glVertex3f(1.1, 1.1, 0);
    glTexCoord2f(0, 1); glVertex3f(-1.1, 1.1, 0);
  glEnd();

  glEnable(GL_DEPTH_TEST);
  glDisable(GL_BLEND);

  mZRot += 0.002;
  mZRot = mZRot > 1 ? 0 : mZRot;

  //calculate fps
  ++mFramesPainted;
  if (mTime.elapsed() > 1000)
  {
    emit fps(mFramesPainted * 1000 / mTime.restart());
    mFramesPainted = 0;
  }
}

void GLWidget::keyPressEvent(QKeyEvent *e)
{
  switch (e->key())
  {
  case Qt::Key_M:
    mMasking = !mMasking;
    qDebug() << "masking = " << mMasking;
    break;
  default:
    QGLWidget::keyPressEvent(e);
  }
}

void GLWidget::loadGLTexture(const QString &filename, GLuint& textures)
{
  //load image
  QImage temp(filename);
  if (temp.isNull())
  {
    temp = QImage(16, 16, QImage::Format_RGB32);
    temp.fill(Qt::green);
    qDebug() << "Can't find image";
  }
  QImage glformatted(QGLWidget::convertToGLFormat(temp)); //convert to gl format

  glGenTextures(1, &textures); //create 1 texture

  glBindTexture(GL_TEXTURE_2D, textures); //create 2d texture at ret
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //filter when image is smaller than texture: good filter
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //filter when image is larger than texture: good filter
  glTexImage2D(GL_TEXTURE_2D, //create 2d texture
               0, //level of detail
               3, //number of data components (rgb = 3)
               glformatted.width(), glformatted.height(), //size of texture
               0, //border
               GL_RGBA, //use rgb in this order
               GL_UNSIGNED_BYTE, //image data is unsigned bytes
               glformatted.bits()); //pointer to image data
}
